package cn.com.libertymutual.aop;

import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.jboss.logging.MDC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import cn.com.libertymutual.core.annotation.SystemValidate;
import cn.com.libertymutual.core.base.dto.RequestBaseDto;
import cn.com.libertymutual.core.base.dto.UserInfo;
import cn.com.libertymutual.core.redis.util.RedisUtils;
import cn.com.libertymutual.core.util.BeanUtilExt;
import cn.com.libertymutual.core.util.Constants;
import cn.com.libertymutual.core.util.Current;
import cn.com.libertymutual.core.web.ServiceResult;
import cn.com.libertymutual.sp.biz.IdentifierMarkBiz;
//import cn.com.libertymutual.service.sys.api.ISystemLogService;
import io.swagger.annotations.ApiOperation;

@Aspect
@Order(4) // 加载顺序 先加载小的,再 加载大的
@Component
public class LogAspect {

	private static Logger log = LoggerFactory.getLogger(LogAspect.class);
	@Resource
	private RedisUtils redisUtils;
	@Resource
	private IdentifierMarkBiz identifierMarkBiz;

	// @Resource private ISystemLogService systemLogService;
	// 如果没有设置备案号,则约定传值N
	// private TpRequestLog tpRequestLog= null;

	private final List<String> EXCLUDE_GEN_TOKEN_FUN = Lists.newArrayList("QueryPayMentAction.subPayment(..)", "QueryPayMentAction.telindex(..)",
			"QueryPayMentAction.productionSuccess(..)", "LdapAction.modifyPwd(..)");

	// Action层切点 cn.com.libertymutual.component.actions
	// @Pointcut("execution (* cn.com.libertymutual..action..*.*(..)) or execution(*
	// cn.com.libertymutual.*.action..*.*(..))")
	@Pointcut("execution (* cn.com.libertymutual..action..*.*(..))  ")
	public void actionAspect() {
		log.info("go into actionAspect");
	}

	/**
	 * 环绕通知
	 * 1.验证请求用户
	 * 2.控制请求次数
	 * 3.设置上下文用户信息 
	 * @param joinPoint 切点 
	 */
	@Around("actionAspect()")
	public Object around(JoinPoint joinPoint) {
		Object reObj = null;
		try {
			// 初始化返回信息
			Current.responseMsg.set(new StringBuilder());
			// log.info("前端请求参数类型 =====>{}", joinPoint);
			Signature signature = joinPoint.getSignature();
			// 设置不拦截的类
			if (signature == null || !EXCLUDE_GEN_TOKEN_FUN.contains(signature.toShortString())) {
				// 设置用户信息
			}
			HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
			String path = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("/"), request.getRequestURI().length());

			/// Current.IP.set(RequestUtils.getIpAddr2(request));
			UserInfo userInfo = (UserInfo) request.getSession().getAttribute(Constants.USER_INFO_KEY);
			if (null != userInfo) {
				Current.userInfo.set(userInfo);
				Current.userId.set(userInfo.getUserId());
			}

			SystemValidate systemValidate = null;
			Class<?> targetClass = joinPoint.getTarget().getClass();
			// 请求方法名
			String methodName = joinPoint.getSignature().getName();
			// 请求参数
			Object[] arguments = joinPoint.getArgs();
			// 方法列表
			Method[] methods = targetClass.getDeclaredMethods();
			for (Method method : methods) {
				// 当前请求方法且参数个数匹配
				if (method.getName().equals(methodName) && method.getParameterTypes().length == arguments.length) {

					systemValidate = method.getAnnotation(SystemValidate.class);
					// 默认无该注解或注解为true时需要校验接口
					if (systemValidate == null || systemValidate.validate()) {
						// 校验接口是否非法访问
						// 校验-请求码
						ServiceResult sr = identifierMarkBiz.verifyIdentCode(request);
						log.info("接口{}校验=====>{}", path, sr.getResult());
						if (sr.getState() == ServiceResult.STATE_SUCCESS) {
							// 允许执行目标方法
							reObj = ((ProceedingJoinPoint) joinPoint).proceed();
						} else {
							// 禁止执行目标方法
							reObj = sr;
						}
					} else {
						// 允许执行目标方法
						reObj = ((ProceedingJoinPoint) joinPoint).proceed();
					}
					break;
				}
			}
			this.logPrint(reObj, request, path);
		} catch (Throwable e) {
			log.error(e.getMessage(), e);
		} finally {
			Current.remove();
		}

		return reObj;
	}

	private void logPrint(Object reObj, HttpServletRequest request, String path) {
		// 日志打印控制
		boolean isPrintLog = true;
		String URI = request.getRequestURI();
		if (URI.indexOf("/cxb/nol/") >= 0) {
			// 以下controller无需打印返回结果
			for (String name : Constants.LOG_NOT_PRINT_CONTROLLER_NAME) {
				if (URI.indexOf(name) >= 0) {
					isPrintLog = false;
				}
			}
		}
		if (isPrintLog) {
			log.info("接口{}返回内容 =====>{}", path, BeanUtilExt.toJsonString(reObj));
		}
	}

	/**
	 * 前置通知 用于拦截Controller层记录用户的操作 
	 * 
	 * @param joinPoint 切点 
	 */
	@Before("actionAspect()")
	public void doBefore(JoinPoint joinPoint) {
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		if (attributes == null)
			return;
		HttpServletRequest request = attributes.getRequest();
		String path = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("/"), request.getRequestURI().length());
		Map<String, Object> map = Maps.newHashMap();
		map.put("requestURL", request.getRequestURL().toString());
		map.put("requestURI", request.getRequestURI());
		map.put("queryString", request.getQueryString());
		map.put("remoteAddr", request.getRemoteAddr());
		map.put("remoteHost", request.getRemoteHost());
		map.put("remotePort", request.getRemotePort());
		map.put("localAddr", request.getLocalAddr());
		map.put("localName", request.getLocalName());
		map.put("method", request.getMethod());
		map.put("headers", getHeadersInfo(request));
		map.put("classMethod", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
		// map.put("args", Arrays.toString(joinPoint.getArgs()));
		String requestParameters = BeanUtilExt.toJsonString(request.getParameterMap());
		map.put("parameters", requestParameters);
		Current.requestParameters.set(requestParameters);
		for (Object o : joinPoint.getArgs()) {
			if (o instanceof RequestBaseDto) {
				// RequestBaseDto reqDto = (RequestBaseDto) o;
				String reqDtoStr = BeanUtilExt.toJsonString(o);
				log.info("接口{}第三方请求信息 =====>{}", path, reqDtoStr);
				break;
			}
		}
		// log.info("前端请求参数 =====>{}", requestParameters);
		log.info("前端接口{}请求明细 =====> {}", path, map);
		return;
	}

	// 暂时未起效
	public static ApiOperation getMthodSystemLog(JoinPoint joinPoint) {
		Class<?> targetClass = joinPoint.getTarget().getClass();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Method[] methods = targetClass.getDeclaredMethods();
		ApiOperation sysLog = null;
		for (Method method : methods) {
			if (method.getName().equals(methodName) && method.getParameterTypes().length == arguments.length) {
				ApiOperation sysLogm = method.getAnnotation(ApiOperation.class);
				if (sysLogm != null) {
					sysLog = sysLogm;
					log.info("前端请求方法名 =====>{}", sysLog.value());
				}
				break;
			}
		}
		return sysLog;
	}

	/*
	 * @After("actionAspect()") public void after(JoinPoint joinPoint) {
	 * 
	 * getMthodDescription( joinPoint );
	 * 
	 * }
	 */

	/*
	 * public static void getMthodDescription( JoinPoint joinPoint ) { Class<?>
	 * targetClass = joinPoint.getTarget().getClass(); String methodName =
	 * joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs();
	 * Method[] methods = targetClass.getDeclaredMethods(); for (Method method :
	 * methods) { if (method.getName().equals(methodName)) { if
	 * (method.getParameterTypes().length == arguments.length) { for (Object argObj
	 * : arguments) { if( argObj != null ) { if( argObj instanceof Class ) {
	 * log.info( argObj.getClass().getName() ); } else
	 * log.info(BeanUtilExt.toJsonString(argObj) ); } else log.warn(null); } break;
	 * } } }
	 * 
	 * }
	 */

	/**
	 * 获取公共请求头	
	 * @param joinPoint
	 * @return
	 */
	public RequestBaseDto getRequestBaseDto(JoinPoint joinPoint) {
		RequestBaseDto reqDto = null;
		for (Object o : joinPoint.getArgs()) {
			if (o instanceof RequestBaseDto) {
				reqDto = (RequestBaseDto) o;
				Current.flowId.set(reqDto.getFlowId());
				MDC.put("userid", Current.flowId.get());
				break;
			}
		}
		return reqDto;
	}

	private Map<String, String> getHeadersInfo(HttpServletRequest request) {
		Map<String, String> map = Maps.newHashMap();
		@SuppressWarnings("rawtypes")
		Enumeration headerNames = request.getHeaderNames();
		while (headerNames.hasMoreElements()) {
			String key = (String) headerNames.nextElement();
			String value = request.getHeader(key);
			map.put(key, value);
		}
		return map;
	}

}
